/*
 *  Created by Wu Heshun on 09-3-10.
 *  Copyright 2009 handcn. All rights reserved.
 *
 */
#ifndef _game_obj_config_h_
#define _game_obj_config_h_

#include "graphic_public.h"

typedef struct {
	static const int VERT_NUM = 4;
	cpVect pos;
	
	float_t density;
	
	//Chipmunk uses the Coulomb friction model, a value of 0.0 is frictionless
	float_t friction; 
	//Elasticity of the shape. A value of 0.0 gives no bounce, while a value of 1.0 will give a "perfect" bounce.
	float_t elasticity;
	
	const char *texture_name;
	
	bool is_movable;
	
	bool is_polygon;	
	cpVect verts[VERT_NUM];
	cpVect texture_coords[VERT_NUM];
	bool is_clippable;
	
	float r;
}obj_info_t;

typedef struct 
	{
	int obj1, obj2;
	cpVect anchr1;
	cpVect anchr2;
	float_t min;
	float_t max;
}rope_info;

#include <chipmunk_object.h>

template <typename T>
struct _create_obj
{
	static inline T* create(SpaceManger * spaceManager, obj_info_t *obj_info)
	{
		//BOOST_STATIC_ASSERT(0);
		return NULL;
	}
};

//CircleAnimationObject
template <typename T>
inline T* create_circle_obj(SpaceManger * spaceManager, obj_info_t *obj_info)
{
	T *ret = NULL;
	
	//get a compiling time error, if T's type is not CircleObject or CircleAnimationObject.
	BOOST_STATIC_ASSERT( (boost::is_same<CircleObject, T>::value) ||  (boost::is_same<CircleAnimationObject, T>::value));
	ASSERT(!obj_info->is_polygon);
	ASSERT(obj_info->is_movable);
	
	ret =  spaceManager->m_ObjManager->create<T>();
	cpFloat mass = M_PI * obj_info->r * obj_info->r * obj_info->density;
	ret->Initialize(spaceManager->m_space, mass, obj_info->r, cpvzero, obj_info->pos);
	ret->setTexture(obj_info->texture_name);
	ret->SetDefaultTextCoords();
	ret->SetActions();
	ret->SetFrictionAndElasticity(obj_info->friction, obj_info->elasticity);
	return ret;
}

template <> struct _create_obj<CircleAnimationObject>
{
	static inline CircleAnimationObject* create(SpaceManger * spaceManager, obj_info_t *obj_info) 
	{
		return create_circle_obj<CircleAnimationObject>(spaceManager, obj_info);
	}
};
template <> struct _create_obj<CircleObject>
{
	static inline CircleObject* create(SpaceManger * spaceManager, obj_info_t *obj_info) 
	{
		return create_circle_obj<CircleObject>(spaceManager, obj_info);
	}
};

template <> struct _create_obj<StaticCircleObject>
{
	static inline StaticCircleObject* create(SpaceManger * spaceManager, obj_info_t *obj_info)
	{
		StaticCircleObject *ret = NULL;
		ASSERT(!obj_info->is_polygon);
		ASSERT(!obj_info->is_movable);
		
		ret =  spaceManager->m_ObjManager->create<StaticCircleObject>();
		ret->Initialize(spaceManager->m_space, obj_info->r, cpvzero, obj_info->pos);
		ret->setTexture(obj_info->texture_name);
		ret->SetDefaultTextCoords();
		ret->SetActions();
		ret->SetFrictionAndElasticity(obj_info->friction, obj_info->elasticity);
		return ret;
	}
};

template <> struct _create_obj<PolygonClipableObject> {
	static inline PolygonClipableObject* create(SpaceManger * spaceManager, obj_info_t *obj_info) {
		PolygonClipableObject *ret = NULL;
		ASSERT(obj_info->is_polygon);
		ASSERT(obj_info->is_movable);
		ASSERT(obj_info->is_clippable);		
		ret =  spaceManager->m_ObjManager->create<PolygonClipableObject>();
		
		float_t area;
		point2d_t _verts[obj_info_t::VERT_NUM];
		for(int i = 0; i < obj_info_t::VERT_NUM; i++) _verts[i] = obj_info->verts[i].x, obj_info->verts[i].y;
		point2d_t gravity_center = polygon::GetPolygonCOG(_verts, obj_info_t::VERT_NUM, area);		
		for(int i = 0; i < obj_info_t::VERT_NUM; i++) {
			obj_info->verts[i].x -= gravity_center[0];
			obj_info->verts[i].y -= gravity_center[1];
		}
		
		cpFloat mass = area * obj_info->density;
		ret->Initialize(spaceManager->m_space, mass, obj_info_t::VERT_NUM, obj_info->verts, cpvzero, cpv(gravity_center[0], gravity_center[1]));
		
		CGSize size = CGSizeMake(fabs(obj_info->verts[0].x) * 2, fabs(obj_info->verts[0].y) * 2);
		Texture2D * tex = getCombineTex(NULL, size, boost::bind(draw_cheese, size));
		ret->setTexture(tex);
		[tex release];
		ret->SetDefaultTextCoords();
		//		ret->setTexture(obj_info->texture_name);
		//		ret->SetDefaultTextCoords();
		
		
		ret->SetActions();
		ret->SetFrictionAndElasticity(obj_info->friction, obj_info->elasticity);
		return ret;
	}
};

template <> struct _create_obj<PolygonUnclipableObject> 
{
	static inline PolygonUnclipableObject* create(SpaceManger * spaceManager, obj_info_t *obj_info)
	{
		PolygonUnclipableObject *ret = NULL;
		ASSERT(obj_info->is_polygon);
		ASSERT(obj_info->is_movable);
		ASSERT(!obj_info->is_clippable);		
		ret =  spaceManager->m_ObjManager->create<PolygonUnclipableObject>();
		
		float_t area;
		point2d_t _verts[obj_info_t::VERT_NUM];
		for(int i = 0; i < obj_info_t::VERT_NUM; i++) _verts[i] = obj_info->verts[i].x, obj_info->verts[i].y;
		point2d_t gravity_center = polygon::GetPolygonCOG(_verts, obj_info_t::VERT_NUM, area);		
		for(int i = 0; i < obj_info_t::VERT_NUM; i++) {
			obj_info->verts[i].x -= gravity_center[0];
			obj_info->verts[i].y -= gravity_center[1];
		}
		
		cpFloat mass = area * obj_info->density;
		ret->Initialize(spaceManager->m_space, mass, obj_info_t::VERT_NUM, obj_info->verts, cpvzero, cpv(gravity_center[0], gravity_center[1]));
		
		if(0 == strcmp(obj_info->texture_name, IMG_CHOCOLATE)) {
			float_t p0p1 = sqrtf(cpvdot(cpvsub(obj_info->verts[1], obj_info->verts[0]), cpvsub(obj_info->verts[1], obj_info->verts[0])));
			float_t p1p2 = sqrtf(cpvdot(cpvsub(obj_info->verts[2], obj_info->verts[1]), cpvsub(obj_info->verts[2], obj_info->verts[1])));
			if (p0p1 > p1p2) {
				CGSize size = CGSizeMake(p0p1, p1p2);
				Texture2D * tex = getCombineTex(NULL, size, boost::bind(draw_chocolate, size));
				ret->setTexture(tex);
				[tex release];
				ret->SetDefaultTextCoords(false);
			}
			else {
				CGSize size = CGSizeMake(p1p2, p0p1);
				Texture2D * tex = getCombineTex(NULL, size, boost::bind(draw_chocolate, size));
				ret->setTexture(tex);
				[tex release];
				ret->SetDefaultTextCoords(true);
			}
		}
		else {
			ret->setTexture(obj_info->texture_name);
			ret->SetDefaultTextCoords();
		}
		
		ret->SetActions();
		ret->SetFrictionAndElasticity(obj_info->friction, obj_info->elasticity);
		return ret;
	}
};

template <> struct _create_obj<StaticPolygonObject> {
	static inline StaticPolygonObject* create(SpaceManger * spaceManager, obj_info_t *obj_info) {
		StaticPolygonObject *ret = NULL;
		ASSERT(obj_info->is_polygon);
		ASSERT(!obj_info->is_movable);		
		ret =  spaceManager->m_ObjManager->create<StaticPolygonObject>();
		
		float_t area;
		point2d_t _verts[obj_info_t::VERT_NUM];
		for(int i = 0; i < obj_info_t::VERT_NUM; i++) _verts[i] = obj_info->verts[i].x, obj_info->verts[i].y;
		point2d_t gravity_center = polygon::GetPolygonCOG(_verts, obj_info_t::VERT_NUM, area);		
		for(int i = 0; i < obj_info_t::VERT_NUM; i++) 
		{
			obj_info->verts[i].x -= gravity_center[0];
			obj_info->verts[i].y -= gravity_center[1];
		}
		
		ret->Initialize(spaceManager->m_space, obj_info_t::VERT_NUM, obj_info->verts, cpvzero, cpv(0, 0));
		
		ret->m_pBoby->p = cpv(gravity_center[0], gravity_center[1]);
		
		float_t p0p1 = sqrtf(cpvdot(cpvsub(obj_info->verts[1], obj_info->verts[0]), cpvsub(obj_info->verts[1], obj_info->verts[0])));
		float_t p1p2 = sqrtf(cpvdot(cpvsub(obj_info->verts[2], obj_info->verts[1]), cpvsub(obj_info->verts[2], obj_info->verts[1])));
		if (p0p1 > p1p2) {
			CGSize size = CGSizeMake(p0p1, p1p2);
			Texture2D * tex = getCombineTex(NULL, size, boost::bind(draw_chocolate, size));
			ret->setTexture(tex);
			[tex release];
			ret->SetDefaultTextCoords(false);
		}
		else {
			CGSize size = CGSizeMake(p1p2, p0p1);
			Texture2D * tex = getCombineTex(NULL, size, boost::bind(draw_chocolate, size));
			ret->setTexture(tex);
			[tex release];
			ret->SetDefaultTextCoords(true);
		}
		
		ret->SetActions();
		ret->SetFrictionAndElasticity(obj_info->friction, obj_info->elasticity);
		return ret;
	}
};

template <typename T>
inline T* CreateObj(SpaceManger * spaceManager, obj_info_t *obj_info, std::vector<cpBody*> &body) {
	obj_info_t tmp = *obj_info;
	T *ret = _create_obj<T>::create(spaceManager, &tmp);
	body.push_back(ret->m_pBoby);
	return ret;
}


#endif //_scence_config_h_
